package PlotPourri.GUI;

import PlotPourri.Database.DatabaseLMCjhk;
import PlotPourri.*;
import PlotPourri.Database.StarDatabase;
import com.panayotis.gnuplot.JavaPlot;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;

public class GUIjhk extends JFrame implements MouseListener, ActionListener, ListSelectionListener, KeyListener {

    StarDatabase starDatabase;
    JTable starsTable;
    SortableTableModel starsTableModel;
    JTextField searchTextField;
    JFormattedTextField distanceText, dispersionText, oscillationText;
    JSpinner orderSpinner, dataPointsSpinner;
    DefaultListModel datfileListModel;
    JList datfileList;
    JScrollPane starsScroll, datfileScroll;
    JPanel mainPanel;
    JMenuItem saveMenuItem, quitMenuItem, aboutMenuItem;
    JMenuBar menuBar;
    JCheckBox bestFit, bestOrder;
    JButton plotButton;
    List<BandRadioButton> bandRadioList;
    ButtonGroup bandGroup, optionsGroup;
    final String[] starsColumnNames = {"Star", "Period", "CL"};
    final File cpapirFile = new File("/home/earl/Documents/Research/LMC/JHK/cpapir.dat");
    final File datfilesDir = new File("/home/earl/Documents/Research/LMC/JHK/lc");

    public GUIjhk() {

        setTitle("PlotPourri");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JMenu fileMenu = new JMenu("File");
        saveMenuItem = new JMenuItem("Save Plot");
        saveMenuItem.addActionListener(this);
        saveMenuItem.setEnabled(false);
        quitMenuItem = new JMenuItem("Quit");
        quitMenuItem.addActionListener(this);
        fileMenu.add(saveMenuItem);
        fileMenu.addSeparator();
        fileMenu.add(quitMenuItem);

        JMenu helpMenu = new JMenu("Help");
        aboutMenuItem = new JMenuItem("About");
        aboutMenuItem.addActionListener(this);
        helpMenu.add(aboutMenuItem);

        menuBar = new JMenuBar();
        menuBar.add(fileMenu);
        menuBar.add(helpMenu);

        starDatabase = new DatabaseLMCjhk(cpapirFile, datfilesDir);
        List<Object[]> data = new ArrayList<Object[]>();
        for (int name : starDatabase.keySet()) {
            Star star = starDatabase.get(name);
            data.add(new Object[] {name, star.getPeriod(), star.getCl().toString()});
        }
        starsTableModel = new SortableTableModel(starsColumnNames, data.toArray(new Object[data.size()][3]));
        starsTable = new JTable(starsTableModel);
        starsTable.setSelectionMode(0);
        starsTable.setRowSorter(new TableRowSorter(starsTableModel));
        starsTable.getColumnModel().getColumn(1).setCellRenderer(new PeriodColumnRenderer());
        starsTable.getSelectionModel().addListSelectionListener(this);
        starsTable.getColumnModel().getSelectionModel().addListSelectionListener(this);
        starsScroll = new JScrollPane(starsTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

        searchTextField = new JTextField();
        searchTextField.addKeyListener(this);
        searchTextField.setColumns(1);

        Box searchBox = new Box(BoxLayout.X_AXIS);
        searchBox.add(new JLabel(" Search "));
        searchBox.add(searchTextField);

        bandGroup = new ButtonGroup();
        Box bandBox = new Box(BoxLayout.X_AXIS);
        bandRadioList = new ArrayList<BandRadioButton>();
        for (int i = 0; i < BandName.getJHK().length; i++) {
            BandName bandName = BandName.getJHK()[i];
            BandRadioButton band = new BandRadioButton(bandName.toString() + " Band", bandName);
            band.addActionListener(this);
            if (i == 0) band.setSelected(true);
            bandGroup.add(band);
            bandBox.add(band);
            bandRadioList.add(band);
        }

        bestFit = new JCheckBox("Best-Fit Line ");
        bestFit.setSelected(true);
        bestFit.addActionListener(this);

        bestOrder = new JCheckBox("Find Best Order");
        bestOrder.setSelected(true);
        bestOrder.addActionListener(this);

        Box optionsBox = new Box(BoxLayout.X_AXIS);
        optionsBox.add(bestFit);
        optionsBox.add(bestOrder);

        datfileListModel = new DefaultListModel();
        datfileList = new JList(datfileListModel);
        datfileList.addListSelectionListener(this);
        datfileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        datfileScroll = new JScrollPane(datfileList, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

        plotButton = new JButton(" Plot ");
        plotButton.addMouseListener(this);
        plotButton.setEnabled(false);

        Integer[] tmp = new Integer[200];
        for (int i = 1; i < tmp.length; i++) tmp[i] = i;
        dataPointsSpinner = new JSpinner(new SpinnerListModel(tmp));
        dataPointsSpinner.setValue(new Integer(100));
        dataPointsSpinner.setEnabled(true);

        orderSpinner = new JSpinner(new SpinnerListModel(new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}));
        orderSpinner.setValue(new Integer(1));
        orderSpinner.setEnabled(false);

        dispersionText = new JFormattedTextField(NumberFormat.getNumberInstance());
        dispersionText.setValue(new Double(5));
        dispersionText.addActionListener(this);

        oscillationText = new JFormattedTextField(NumberFormat.getNumberInstance());
        oscillationText.setValue(new Double(0.015));
        oscillationText.addActionListener(this);

        distanceText = new JFormattedTextField(NumberFormat.getNumberInstance());
        distanceText.setValue(new Double(3000));
        distanceText.addActionListener(this);

        Box dispersionBox = new Box(BoxLayout.X_AXIS);
        dispersionBox.add(new JLabel(" Dispersion Tolerance "));
        dispersionBox.add(dispersionText);

        Box oscillationBox = new Box(BoxLayout.X_AXIS);
        oscillationBox.add(new JLabel(" Oscillation Tolerance "));
        oscillationBox.add(oscillationText);

        Box distanceBox = new Box(BoxLayout.X_AXIS);
        distanceBox.add(new JLabel(" Distance Tolerance "));
        distanceBox.add(distanceText);

        Box toleranceBox = new Box(BoxLayout.Y_AXIS);
        toleranceBox.add(dispersionBox);
        toleranceBox.add(oscillationBox);
        toleranceBox.add(distanceBox);

        Box plotBox = new Box(BoxLayout.X_AXIS);
        plotBox.add(new JLabel(" Points "));
        plotBox.add(dataPointsSpinner);
        plotBox.add(new JLabel(" Order "));
        plotBox.add(orderSpinner);
        plotBox.add(plotButton);

        Container left = new JPanel(new BorderLayout());
        left.add(starsScroll, BorderLayout.CENTER);
        left.add(searchBox, BorderLayout.PAGE_END);
        left.setPreferredSize(new Dimension(250, 300));

        Container bottom = new JPanel(new BorderLayout());
        bottom.add(optionsBox, BorderLayout.PAGE_START);
        bottom.add(toleranceBox, BorderLayout.CENTER);
        bottom.add(plotBox, BorderLayout.PAGE_END);

        Container right = new JPanel(new BorderLayout());
        right.add(bandBox, BorderLayout.PAGE_START);
        right.add(datfileScroll, BorderLayout.CENTER);
        right.add(bottom, BorderLayout.PAGE_END);
        right.setPreferredSize(new Dimension(250, 300));

        JSplitPane allPanes = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);

        add(allPanes);
        pack();
        setJMenuBar(menuBar);
        setResizable(true);
        setVisible(true);
        setLocationRelativeTo(null);
    }

    void getDatfileListModel() {
        if (starsTable.getSelectedRow() < 0) {
            datfileList.setModel(new DefaultListModel());
            plotButton.setEnabled(false);
            return;
        }
        Star star = starDatabase.get((Integer) starsTable.getRowSorter().getModel().getValueAt(starsTable.convertRowIndexToModel(starsTable.getSelectedRow()), 0));
        datfileListModel = new DefaultListModel();
        for (Observation datfile : star.getBand(getBand()))
            datfileListModel.addElement(datfile.getName());
        datfileList.setModel(datfileListModel);
        plotButton.setEnabled(true);
        orderSpinner.setEnabled(!bestOrder.isSelected() && bestFit.isSelected() && datfileList.getLastVisibleIndex() >= 0);
        saveMenuItem.setEnabled(true);
    }

    public void actionPerformed(ActionEvent evt) {
        if (evt.getSource() == quitMenuItem) System.exit(0);
        if (evt.getSource() == saveMenuItem) {
            JFileChooser fc = new JFileChooser();
            int returnVal = fc.showOpenDialog(this);
            //File file = null;
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                Star star = starDatabase.get((Integer) starsTable.getRowSorter().getModel().getValueAt(starsTable.convertRowIndexToModel(starsTable.getSelectedRow()), 0));
                int[] indices = getSelectedIndices();
                Band list = getIndicesList(star, indices);
                if (bestOrder.isSelected()) list = getOrderedList(list, indices);
                if (list.size() == 0) return;
                String script = "reset\nunset key\nunset colorbox\nunset log\nunset label\nset autoscale\n\nplot ";
                for (int i = 0; i < list.size(); i++) if (i < list.size() - 1) script += "\"" + list.get(i).getPath() + "\" using 1:2, ";
                    else script += "\"" + list.get(i).getPath() + "\" using 1:2";
                if (bestFit.isSelected()) {
                    script += ", \"-\" with lines";
                    double[][] tmp = list.combine().getData(Integer.parseInt(orderSpinner.getModel().getValue() + ""), Integer.parseInt(dataPointsSpinner.getModel().getValue() + ""));
                    for (int i = 0; i < tmp.length; i++) script += "\n" + tmp[i][0] + " " + tmp[i][1];
                    script += "\ne";
                }
                File file = new File(fc.getSelectedFile().getPath());
                try {
                    if (file.exists()) file.createNewFile();
                    BufferedWriter writer = new BufferedWriter(new FileWriter(file));
                    writer.write(script);
                    writer.close();
                } catch (IOException ex) {
                    System.err.print(ex);
                }
            }
        }
        if (evt.getSource() == bestFit)
            if (!bestFit.isSelected()) {
                bestOrder.setEnabled(false);
                orderSpinner.setEnabled(!bestOrder.isSelected() && bestFit.isSelected() && datfileList.getLastVisibleIndex() >= 0);
                dispersionText.setEnabled(false);
                oscillationText.setEnabled(false);
                distanceText.setEnabled(false);
                dataPointsSpinner.setEnabled(false);
            } else {
                bestOrder.setEnabled(true);
                dataPointsSpinner.setEnabled(true);
                bestOrder();
            }
        if (evt.getSource() == bestOrder) bestOrder();
        for (BandRadioButton bandButton : bandRadioList) if (evt.getSource() == bandButton) getDatfileListModel();
    }

    public void bestOrder() {
        boolean boselected = bestOrder.isSelected();
        orderSpinner.setEnabled(!boselected && bestFit.isSelected() && datfileList.getLastVisibleIndex() >= 0);
        dispersionText.setEnabled(boselected);
        oscillationText.setEnabled(boselected);
        distanceText.setEnabled(boselected);
    }

    public void valueChanged(ListSelectionEvent evt) {
        if (!evt.getValueIsAdjusting() && evt.getSource() == starsTable.getSelectionModel()) getDatfileListModel();
    }

    public BandName getBand() {
        for (BandRadioButton button : bandRadioList) if (button.isSelected()) return button.getBandName();
        return null;
    }

    public void keyPressed(KeyEvent evt) {
    }

    public void keyReleased(KeyEvent evt) {
        if (evt.getSource() == searchTextField) ((TableRowSorter) starsTable.getRowSorter()).setRowFilter(RowFilter.regexFilter(searchTextField.getText()));
    }

    public void keyTyped(KeyEvent evt) {
    }

    public int[] getSelectedIndices() {
        int[] indices = null;
        if (datfileList.isSelectionEmpty()) {
            indices = new int[datfileList.getModel().getSize()];
            for (int i = 0; i < indices.length; i++) indices[i] = i;
            datfileList.setSelectedIndices(indices);
        } else indices = datfileList.getSelectedIndices();
        return indices;
    }

    public Band getIndicesList(Star star, int[] indices) {
        Band list = new Band(getBand(), indices.length);
        if (bestOrder.isSelected())
            for (int i = 0; i < indices.length; i++) {
                Observation datfile = star.getDatfile(Integer.parseInt(datfileListModel.get(indices[i]) + ""), star.getBand(getBand()));
                if (datfile.getDistance() < Double.parseDouble(distanceText.getValue() + "")) list.add(datfile);
            }
        else for (int i = 0; i < indices.length; i++) list.add(star.getDatfile(Integer.parseInt(datfileListModel.get(indices[i]) + ""), star.getBand(getBand())));
        return list;
    }

    public Band getOrderedList(Band list, int[] indices) {
        Band orderedList = list.getOrderedBand(Double.parseDouble(dispersionText.getValue() + ""));
        orderSpinner.getModel().setValue(orderedList.getOrder());
        int[] newIndices = new int[orderedList.size()];
        int counter = 0;
        for (Observation datfile : orderedList) for (int i = 0; i < indices.length; i++) if (datfile.getName() == (Integer) datfileList.getModel().getElementAt(indices[i])) newIndices[counter++] = indices[i];
        datfileList.setSelectedIndices(newIndices);
        return orderedList;
    }

    public void mouseClicked(MouseEvent evt) {
        if (evt.getSource() == plotButton) {
            if (starsTable.getSelectedRow() < 0) return;
            Star star = starDatabase.get((Integer) starsTable.getRowSorter().getModel().getValueAt(starsTable.convertRowIndexToModel(starsTable.getSelectedRow()), 0));
            int[] indices = getSelectedIndices();
            Band list = getIndicesList(star, indices);
            if (bestOrder.isSelected()) list = getOrderedList(list, indices);
            if (list.size() == 0) return;
            JavaPlot p = new JavaPlot();
            for (double[][] obsData : list.getPlots()) p.addPlot(obsData);
            if (bestFit.isSelected()) {
                p.addPlot(list.combine().getData(Integer.parseInt(orderSpinner.getModel().getValue() + ""), Integer.parseInt(dataPointsSpinner.getModel().getValue() + "")));
                p.setTitle("Dispersion: " + list.combine().getDisp(Integer.parseInt(orderSpinner.getModel().getValue() + ""), Integer.parseInt(dataPointsSpinner.getModel().getValue() + ""), Double.parseDouble(oscillationText.getValue() + "")));
            }
            p.getParameters().getPostInit().add("set key left top");
            p.plot();
        }
    }

    public void mouseExited(MouseEvent evt) {
    }

    public void mousePressed(MouseEvent evt) {
    }

    public void mouseReleased(MouseEvent evt) {
    }

    public void mouseEntered(MouseEvent evt) {
    }

    public static void createAndShowGUI() {
        JFrame.setDefaultLookAndFeelDecorated(false);
        try {
            //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }
        // Launch the GUI
        GUIjhk msa = new GUIjhk();
    }

    public static void main(String[] args) {
        // schedule a job for the event dispatch thread, creating and showing this application's GUI
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }
}
